function BuildAtom(item, task) {
  this.parent = item;
  this.type = "build_atom";
  if (task.options && task.options.isotope) {
    this.options = task.options;
  } else {
    this.options = { isotope : "He-4", charge : 0, build : "wa" };
  }
  this.attempts = 0;
  this.mouse = Task.prototype.mouse;
}

var NUMBER_OF_EACH_PARTICLE = 6;

BuildAtom.prototype = {
  scale : 120,

  edit : function() {
    this.show($("#item .content"));
    this.showOptions();
  },

  show : function(target) {
    Task.prototype.show.apply(this, arguments);

    this.showAtom();
    this.showParticles();
    this.easel.append("<div class='p-absolute' style='right: -8px; bottom: -8px;'>" +
      "<span>When finished click the check mark to check your answer&nbsp;&nbsp;&rarr;</span>&nbsp;&nbsp;" +
      "<a class='score-button' enabled='false' onclick='lesson.items.current.task.score(false);'>" +
      "<img src='/images/apply_gray.png' style='opacity: 0.5; vertical-align: middle;'/>" +
      "</a></div>");
    this.initializeMouseHandlers();
  },

  showAtom : function() {
    var e = this.easel;
    e.append("<canvas class='atom p-absolute' width='360px' height='360px'" +
      "style='left:20px; top:0;'></canvas>");
    e.atom = { protons : 0, neutrons : 0, electrons : 0 };
    e.atom.canvas = e.find("canvas.atom");
    var atom = e.atom.canvas;

    if (this.options.build == "no") {
      e.append("<div class='target'></div>")
    } else {
      var ctx = atom[0].getContext('2d');
      var x = 0.5 * ctx.canvas.width;
      var y = 0.5 * ctx.canvas.height;
      i2a("?").drawShell(ctx, x, y, this.scale);
    }
    var base = this.options.build != "eo" ? this.options.base : this.options.isotope;
    if (base && base != "none") {
      var bi = i2a(base);
      atom.w = atom.attr("width").substring(0, atom.attr("width").length - 2);
      atom.h = atom.attr("height").substring(0, atom.attr("height").length - 2);
      var left = atom.position().left + 0.5 * atom.w;
      var top = atom.position().top + 0.5 * atom.h;
      for (var i = 0; i < bi.protons; i++) {
        e.append("<canvas id='proton-" + i + "' class='particle proton' width='40px' height='40px;'" +
          "style='position: absolute; border-radius:20px; -moz-border-radius: 20px; " +
          "left: " + left + "px; top: " + top + "px;'></canvas>");
        var proton = e.find("canvas#proton-" + i);
        proton.ctx = proton[0].getContext("2d");
        bi.drawProton(proton.ctx, 20, 20, this.scale);
      }
      for (var j = 0; j < bi.neutrons; j++) {
        e.append("<canvas id='neutron-" + j + "' class='particle neutron' width='40px' height='40px;'" +
          "style='position: absolute; border-radius:20px; -moz-border-radius: 20px; " +
          "left: " + left + "px; top: " + top + "px;'></canvas>");
        var neutron = e.find("canvas#neutron-" + j);
        neutron.ctx = neutron[0].getContext("2d");
        bi.drawNeutron(neutron.ctx, 20, 20, this.scale);
      }
      this.positionNucleusParticles();
    }
  },

  showParticles : function() {
    var a = i2a("?");
    var b = this.options.build;
    var e = this.easel;
    var wa = b == "wa";
    var no = b == "no";
    var eo = b == "eo";
    var four = 4;

    for (var i = 0; i < (wa ? 3 : 2) * four; i++) {
      var left = 400 + (i % four) * 80;
      var top = (wa ? 80 : 120) + Math.floor(i / four) * 80;
      var type;
      switch (b) {
        case "no" : type = Math.floor(i / four) == 0 ? "proton" : "neutron"; break;
        case "eo" : type = "electron"; break;
        default :
          type = type = Math.floor(i / four) == 0 ? "proton" : Math.floor(i / four) == 1 ? "neutron" : "electron";
          break;
      }
      e.append("<canvas id='particle-" + i + "' class='particle " + type + "' width='40px' height='40px;'" +
        "style='position: absolute; border-radius:20px; -moz-border-radius: 20px; " +
        "left: " + left + "px; top: " + top + "px;'></canvas>");
      var p = e.find("canvas#particle-" + i);
      p.ctx = p[0].getContext("2d");
      switch (type) {
        case "proton" : a.drawProton(p.ctx, 20, 20, this.scale); break;
        case "neutron" : a.drawNeutron(p.ctx, 20, 20, this.scale); break;
        case "electron" : a.drawElectron(p.ctx, 20, 20, this.scale); break;
      }
    }
  },

  initializeMouseHandlers : function() {
    var data = { task : this };

    var particles = this.easel.find(".particle");
    particles.mousedown(function(event) { lesson.items.current.task.particleMouseDown($(this), event) });
    particles.mouseup(function(event) { lesson.items.current.task.particleMouseUp(event) });
  },

  showOptions : function() {
    Task.prototype.showOptions.apply(this, arguments);
    var target = $("#item .options");
    this.showBuildOptions(target);
    this.showAnswerSelect(target);
    this.showChargeSelect(target);
    this.showBaseSelect(target);
  },

  showBuildOptions : function(target) {
    if (target.find(".build").length == 0) {
      target.append("<div class='build'>Build option : <select></select></div>");
    }
    var build_select = target.find(".build select").html("");
    var build_options = { wa : "Whole atom", no : "Nucleus only", eo : "Electrons only" };
    for (var key in build_options) {
      build_select.append("<option value='" + key + "'>" + build_options[key] + "</option>");
    }
    build_select.change(function() {
      var item = lesson.items.current;
      item.task.options.build = this.value;
      var task = { type : item.task.type, options : item.task.options };
      item.saveProperty("task", JSON.stringify(task));
      item.task.edit();
    });
    var options = build_select.find("option");
    for (var i = 0; i < options.length; i++) {
      if (options[i].value == this.options.build) {
        $(options[i]).attr("selected", "selected");
      }
    }
  },

  showAnswerSelect : function(target) {
    if (target.find(".isotope").length == 0) { target.append("<span class='isotope'></span>"); }
    var isotope_select = target.find(".isotope").html("Answer : <select></select>").find("select");
    for (var j in i2n) { isotope_select.append("<option value='" + j + "'>" + i2n[j] + "</option>"); }
    isotope_select.change(function() {
      var item = lesson.items.current;
      item.task.options.isotope = this.value;
      var task = { type : item.task.type, options : item.task.options };
      item.saveProperty("task", JSON.stringify(task));
      item.task.show();
    });
    var os = isotope_select.find("option");
    for (var i = 0; i < os.length; i++) {
      if (os[i].value == this.options.isotope) {
        $(os[i]).attr("selected", "selected");
      }
    }
  },

  showBaseSelect : function(target) {
    if (target.find(".base").length == 0) { target.append("<div class='base'></div>"); }
    if (this.options.build == "eo") {
      target.find(".base").html("");
      return;
    }
    var base_select = target.find(".base").html(
      "Base atom : <select><option value='none'>none</option></select>").find("select");
    for (var j in i2n) { base_select.append("<option value='" + j + "'>" + i2n[j] + "</option>"); }
    base_select.change(function() {
      var item = lesson.items.current;
      item.task.options.base = this.value;
      var task = { type : item.task.type, options : item.task.options };
      item.saveProperty("task", JSON.stringify(task));
      item.task.show();
    });
    var os = base_select.find("option");
    for (var i = 0; i < os.length; i++) {
      if (os[i].value == this.options.base) {
        $(os[i]).attr("selected", "selected");
      }
    }
  },

  showChargeSelect : function(target) {
    if (target.find(".charge").length == 0) { target.append("<span class='charge'></span>"); }
    var charge_select = target.find(".charge").html("Charge : <select></select>").find("select");
    for (var j = -2; j <= 2;  j++) {
      charge_select.append("<option value='" + j + "'>" + (j > 0 ? "+" : "") + j + "</option>");
    }
    charge_select.change(function() {
      var item = lesson.items.current;
      item.task.options.charge = this.value;
      var task = { type : item.task.type, options : item.task.options };
      item.saveProperty("task", JSON.stringify(task));
      item.task.show();
    });
    var os = charge_select.find("option");
    for (var i = 0; i < os.length; i++) {
      if (os[i].value == this.options.charge) {
        $(os[i]).attr("selected", "selected");
      }
    }
  },

  positionNucleusParticles : function() {
    Task.prototype.enableScoreButton.apply(this, arguments);
    var easel = this.easel;

    var atom = this.easel.atom.canvas;
    atom.left = parseInt(atom.css("left"));
    atom.top = parseInt(atom.css("top"));
    atom.w = parseInt(atom.attr("width"));
    atom.h = parseInt(atom.attr("height"));
    var scale = 0.333 * atom.w;
    var protons = easel.find(".proton").filter(function(index) {
      return 0.8 * scale > getDistanceFromCenter($(this), atom);
    });
    var neutrons = easel.find(".neutron").filter(function(neutron) {
      return 0.8 * scale > getDistanceFromCenter($(this), atom);
    });
    var pix = 0; var nix = 0;
    var proton; var neutron;
    if (protons.length + neutrons.length > 0) {
      var nc = aw2nc[protons.length + neutrons.length];
      for (var j = 0; j * 2 < nc.length; j++) {
        var left = parseInt(atom.left + 0.5 * atom.w - 20 + nc[2 * j] * scale);
        var top = parseInt(atom.top + 0.5 * atom.h - 20 + nc[2 * j + 1] * scale);
        if (pix == protons.length || (j % 2 == 0 && j < neutrons.length * 2)) {
          neutron = $(neutrons[nix]);
          neutron.css({"z-index" : 16 + j});
          if (Math.abs(left - neutron.position().left) > 1
            || Math.abs(top - neutron.position().top) > 1) {
            neutron.animate( { left : left, top : top } );
          }
          nix++;
        } else {
          proton = $(protons[pix]);
          proton.css({"z-index" : 16 + j});
          if (Math.abs(left - proton.position().left) > 1
            || Math.abs(top - proton.position().top) > 1) {
            proton.animate( { left : left, top : top  } );
          }
          pix++;
        }
      }
    }
  },

  positionElectrons : function() {
    Task.prototype.enableScoreButton.apply(this, arguments);

    var atom = this.easel.atom.canvas;

    var electrons = this.easel.find(".electron").filter(function(electron) {
      var max_distance = 1.2 * lesson.items.current.task.scale;
      return max_distance > getDistanceFromCenter($(this), atom);
    });
    if (electrons.length > 0) {
      var a2 = i2a("?");
      atom.ctx = atom[0].getContext("2d");
      atom.ctx.clearRect(0, 0, atom.width(), atom.height());
      if (electrons.length > 2) {
        a2.period = 2;
        a2.drawShell(atom.ctx, 0.5 * atom.width(), 0.5 * atom.height(), this.scale, null, 2);
      }
      a2.drawShell(atom.ctx, 0.5 * atom.width(), 0.5 * atom.height(), this.scale);
    }
    for (var k = 0; k < electrons.length; k++) {
      var e = $(electrons[k]);
      e.left = parseInt(atom.position().left + 0.5 * atom.width() - 20 + ec[2 * k] * this.scale);
      e.top = parseInt(atom.position().top + 0.5 * atom.height() - 20 + ec[2 * k + 1] * this.scale);
      if (Math.abs(e.left - e.position().left) > 1
        || Math.abs(e.top - e.position().top) > 1) {
        e.animate( { left : e.left, top : e.top } );
      }
    }
  },

  score : function(edit) {
    var a = i2a(this.options.isotope);
    var protons = 0;
    var neutrons = 0;
    var electrons = 0;
    var particles = this.easel.find(".particle");
    for (var i = 0; i < particles.length; i++) {
      var p = $(particles[i]);
      var atom = this.easel.atom.canvas;
      var scale = 0.333 * atom.width();
      var r = getDistanceFromCenter(p, atom);
      if (p.hasClass("proton") && r < 0.8 * scale) { protons++; }
      else if (p.hasClass("neutron") && r < 0.8 * scale) { neutrons++; }
      else if (p.hasClass("electron") && r < 1.2 * scale) { electrons++; }
    }
    if (protons != 0 || neutrons != 0 || electrons != 0) {
      this.attempts++;
    }
    var score = { attempts : this.attempts, protons : protons, neutrons : neutrons, electrons : electrons,
      correct : (protons == a.protons && neutrons == a.neutrons
        && (this.options.build == "no" || protons == electrons + Number(this.options.charge)))
    };

    this.showScore(score);
    if (score.correct) {
      this.complete = true;
      this.recordScore(score);
      lesson.showNavigation();
    }
  },

  showScore : function(score) {
    Task.prototype.showScore.apply(this, arguments);

    var atom = i2a(this.options.isotope);
    var name = atom.name.toLowerCase() + "-" + (atom.protons + atom.neutrons);

    this.easel.score.show();
    if (score.protons == 0 && score.neutrons == 0 && score.electrons == 0) {
      this.easel.score.html("<p style='margin-right:64px;'>Your model has no protons, neutrons, or electrons. " +
        "Build an atomic model of " + name + " by dragging the pieces above to the space on the left.</p>" +
        "<a class='retry p-absolute' style='right:16px;bottom:8px;' " +
        "onclick='lesson.items.current.task.easel.score.hide()'><img src='/images/reload.png'></a>");
      return;
    }
    switch (this.options.build) {
      case "eo" : this.showElectronsOnlyScore(atom, score); break;
      case "no" : this.showNucleusOnlyScore(atom, score); break;
      case "wa" : this.showWholeAtomScore(atom, score); break;
      default   : console.error("Unknown build option"); break;
    }

    var onclick = "";
    if (score.correct) {
      onclick = "lesson.items.current.task.easel.score.hide();" + "lesson.showNextSlide();";
      this.easel.score.append("<a class='next p-absolute' style='right:16px;bottom:8px;' " +
        "onclick='" + onclick + "'><img src='/images/forward.png'></a>");
    } else {
      onclick = "lesson.items.current.task.easel.score.hide()";
      this.easel.score.append("<a class='retry p-absolute' style='right:16px;bottom:8px;' " +
        "onclick='" + onclick + "'><img src='/images/reload.png'></a>");
    }
  },

  showElectronsOnlyScore : function(atom, score) {
    var c = this.options.charge;
    var e = atom.protons - Number(c);
    var name = atom.name.toLowerCase() + "-" + (atom.mass);
    var html = "<p>A " + name + " ";
    if (c < 0) {
      html += "anion with " + -c + " negative elementary charge" + (c != -1 ? "s" : "");
    } else if (c == 0) {
      html += "atom";
    } else {
      html += "cation with " + c + " positive elementary charge" + (c != 1 ? "s" : "");
    }
    html += " has " + e + " electron" + (e != 1 ? "s" : "") + " in its electron cloud.</p>";
    if (score.correct) {
      html += "<p style='margin-right:64px;'>The atom you built has the correct number of electrons.</p>";
    } else {
      html += "<p style='margin-right:64px;'>The atom you built has " + score.electrons + " electron" +
        (score.electrons != 1 ? "s" : "") + " in its electron cloud.</p>";
    }
    this.easel.score.html(html);
  },

  showNucleusOnlyScore : function(atom, score) {
    var name = atom.name.toLowerCase() + "-" + (atom.protons + atom.neutrons);
    var html = "<p>A " + name + " nucleus has " + atom.protons + " proton" + (atom.protons != 1 ? "s" : "") +
      " and " + atom.neutrons + " neutron" + (atom.neutrons != 1 ? "s" : "") + ".</p>";
    if (score.correct) {
      html += "<p style='margin-right:64px;'>The nucleus you built has the correct number of protons and neutrons.</p>"
    } else {
      html += "<p style='margin-right:64px;'>The nucleus you built has " + score.protons + " proton" + (atom.protons != 1 ? "s" : "") +
        " and " + score.neutrons + " neutron" + (score.neutrons != 1 ? "s" : "") + ".</p>"
    }
    this.easel.score.html(html);
  },

  showWholeAtomScore : function(atom, score) {
    var name = atom.name.toLowerCase() + "-" + (atom.protons + atom.neutrons);
    this.easel.score.html("<p>A " + name + " atom has " + atom.protons + " proton" + (atom.protons != 1 ? "s" : "") +
      " and " + atom.neutrons + " neutron" + (atom.neutrons != 1 ? "s" : "") + " in its dense nucleus" +
      (this.options.build == "no" ? "." : " and " + atom.protons + " electrons in its electron cloud.") +"</p>");
    if (score.correct) {
      var onclick = "lesson.items.current.task.easel.score.hide();" + "lesson.showNextSlide();";
      if (this.options.build == "no") {
        this.easel.score.append("<p style='margin-right:64px;'>Your model of the " + name +
          " nucleus has the correct number of protons and neutrons. Good job.</p>");
      } else {
        this.easel.score.append("<p style='margin-right:64px;'>Your model of a " + name +
          " atom has the correct number of protons, neutrons and electrons. Good job.</p>");
      }
    } else {
      this.easel.score.append("<p style='margin-right:64px;'>The model you built has " +
        score.protons + " proton" + (score.protons !=  1 ? "s" : "") + " and " +
        score.neutrons + " neutron" + (score.neutrons !=  1 ? "s" : "") + " in its dense nucleus" +
        " and " + score.electrons + " electron" + (score.electrons != 1 ? "s" : "") +
          " in its electron cloud. Try again." + "</p>");
    }
  },

  recordScore : function(score) {
    Task.prototype.recordScore.apply(this, arguments);
  },

  particleMouseDown : function(particle, event) {
    lesson.disableSelection();
    var mouse = { down : true, downEvent : event, target : particle, z : this.mouse.z + 1 };
    mouse.target.left = mouse.target.position().left;
    mouse.target.top = mouse.target.position().top;
    mouse.target.css( { "z-index" : mouse.z } );
    this.mouse = mouse;
  //  alert("mouse down!");
  },

  mouseUp : function(event) {
    var m = this.mouse;
    if (m.target != null && (m.target.hasClass("proton") || m.target.hasClass("neutron"))) {
      this.positionNucleusParticles();
    } else if (m.target != null && m.target.hasClass("electron")) {
      this.positionElectrons();
    }
    this.mouse.down = false;
    this.mouse.target = null;
  },

  mouseMove : function(event) {
    if (this.mouse.down && this.mouse.target) {
      var lesson = $("#lesson");
      var zoom = lesson.length != 0 ? lesson.css("zoom") : 1;
      var target = this.mouse.target;
      var left = Number(target.left) + (event.clientX - this.mouse.downEvent.clientX) / zoom;
      var top = Number(target.top) + (event.clientY - this.mouse.downEvent.clientY) / zoom;
      if (0 < left && left + target.width() < this.easel.width()) { target.css({ left : left })}
      if (0 < top && top + target.height() < this.easel.height()) { target.css({ top : top })}
    }
  },

  particleMouseUp : function(event) {
    var particle = this.mouse.target;
    if (!particle) {
      return;
    } else if (particle.hasClass("electron")) {
      lesson.items.current.task.positionElectrons();
    } else if (particle.hasClass("proton") || particle.hasClass("neutron")) {
      lesson.items.current.task.positionNucleusParticles();
    }
  //  alert("mouse up!");
  }
};

function getDistanceFromCenter(item, target) {
  if (target.position() == null) {
    var z = 0;
    z = 1;
    var a = z;
    z = a + 1;
  }
  item.center = item.position().left + 0.5 * item.width();
  item.middle = item.position().top + 0.5 * item.height();
  target.center = target.position().left + 0.5 * target.width();
  target.middle = target.position().top + 0.5 * target.height();
  var x = item.center - target.center;
  var y = item.middle - target.middle;
  return Math.pow(x * x + y * y, 0.5);
}